"""
@Time    : 2023/09/08 16:08
@File    : code.py
@Software: vscode
@Desc    :code serivice
"""

import random
import string
from typing import Annotated, List

from fastapi import Depends
from sqlalchemy import and_, func, insert, select, update

from forward.api.admin.orm.lucky.activity import ActivityORM
from forward.api.admin.orm.lucky.activity_code_no import ActivityCodeNoORM
from forward.api.admin.orm.lucky.activity_prize import ActivityPrizeORM
from forward.api.admin.orm.lucky.code import CodeORM
from forward.api.admin.orm.lucky.order import ActivityOrderORM
from forward.api.admin.orm.lucky.prize import PrizeORM
from forward.api.admin.orm.merchant import MerchantORM
from forward.api.admin.schemas.lucky.activity import ActivityPrizeSchema
from forward.api.admin.schemas.lucky.code import (
    CodeCompleCodeNoInSchema,
    CodeDetailOutSchema,
    CodeListInSchema,
    CodeStatusInSchema,
)
from forward.common.enums import CodeStatusEnum, PrizeLevelEnum
from forward.dependencies.database import db
from forward.exceptions.base import AppException
from forward.extend.pydantic.result_api import ResultAPI
from forward.http_base import HttpResp
from forward.model.schemas.lucky.activity import (
    ActivityCodeNoSchema,
    ActivityPrizeOutSchema,
)
from forward.model.tables.activity import CodeModel


class CodeService:
    """
    code service
    """

    def __init__(
        self,
        code_orm: Annotated[CodeORM, Depends()],
        activity_orm: Annotated[ActivityORM, Depends()],
        prize_orm: Annotated[PrizeORM, Depends()],
        merchant_orm: Annotated[MerchantORM, Depends()],
        activity_order_orm: Annotated[ActivityOrderORM, Depends()],
        activity_code_no_orm: Annotated[ActivityCodeNoORM, Depends()],
        acitvity_prize_orm: Annotated[ActivityPrizeORM, Depends()],
    ):
        self.code_orm = code_orm
        self.activity_orm = activity_orm
        self.prize_orm = prize_orm
        self.merchant_orm = merchant_orm
        self.activity_order_orm = activity_order_orm
        self.activity_code_no_orm = activity_code_no_orm
        self.activity_prize_orm = acitvity_prize_orm

    async def lists(self, list_in: CodeListInSchema):
        """code 列表

        Args:
            list_in (CodeListInSchema): _description_

        Returns:
            _type_: _description_
        """
        # 查询条件
        where = []
        if list_in.status is not None:
            where.append(CodeModel.status == list_in.status)
        if list_in.prize_id is not None:
            where.append(CodeModel.prize_id == list_in.prize_id)
        if list_in.merchant_id is not None:
            activity_ids = await self.activity_orm.get_ids_by_merchant_id(
                list_in.merchant_id
            )
            where.append(CodeModel.activity_id.in_(activity_ids))
        if list_in.code is not None:
            where.append(CodeModel.code.like(f"%{list_in.code}%"))
        if list_in.code_no is not None:
            prefix = list_in.code_no.split(".")[0]
            code_id = list_in.code_no.split(".")[1]
            where.append(CodeModel.code_id == code_id)
            where.append(CodeModel.prefix == prefix)
        # 分页数据
        pagination = await self.code_orm.lists(
            page_num=list_in.page_no,
            page_size=list_in.page_size,
            where=where,
        )
        lists = []
        # 改造数据
        for item in pagination.lists:
            data = item.model_dump()
            data["code_no"] = (
                f"{item.prefix}.{item.code_id}" if item.prefix else f"No.{item.code_id}"
            )
            data["status_text"] = CodeStatusEnum.get_str(item.status)
            data["prize_level_text"] = PrizeLevelEnum.get_label(item.prize_index)
            code = CodeDetailOutSchema(
                **data,
            )
            lists.append(code)
        return ResultAPI(
            data={
                "lists": lists,
                "count": pagination.total,
            }
        )

    async def status(self, status_in: CodeStatusInSchema):
        print("status_in", status_in)
        statement = (
            update(CodeModel)
            .where(CodeModel.id == status_in.id)
            .values(status_in.model_dump())
        )
        await db.execute(statement)
        return ResultAPI()

    async def generate(self, prefix: str, begin_num: int, end_num: int, num: int):
        """
        params:
            prefix: 码前缀
            begin_num: 开始码
            end_num: 结束码
            num: 生成数量
        """
        # 检测prefix 是否有多余点号
        if prefix.count(".") > 0:
            prefix = prefix.replace(".", "")

        characters = string.ascii_lowercase + string.digits  # 数字和小写字母
        batch_size = 1000  # 每批生成的数量
        total_codes = []
        for num in range(begin_num, end_num + 1):
            # code_id = f"{prefix}.{str(num).zfill(5)}"
            code_id = num
            prefix = prefix
            if await self.code_orm.get_one_by_filters(code_id=code_id):
                # logger.logger.error("码已存在", code_id)
                raise AppException(HttpResp.SYSTEM_ERROR, msg="码已存在")
            code = "".join(random.choice(characters) for _ in range(6))
            total_codes.append({"prefix": prefix, "code_id": code_id, "code": code})
            if len(total_codes) >= batch_size:
                # await self._insert_codes(total_codes)
                await self.code_orm.bulk_create(total_codes)
                total_codes = []
        if total_codes:
            # await self._insert_codes(total_codes)
            await self.code_orm.bulk_create(total_codes)
        return True

    # async def _insert_codes(self, codes):
    #     statement = insert(CodeModel)
    #     await db.execute_many(statement, codes)

    async def check_code_is_empty(
        self, prefix: str, start_code_id: int, end_code_id: int
    ):
        """
        检测码是否为 未使用 CodeStatusEnum.UNUSED
        """
        print("check_code_is_empty", prefix, start_code_id, end_code_id)
        count = end_code_id - start_code_id + 1
        statement = (
            select(func.count(CodeModel.id))
            .where(CodeModel.status == CodeStatusEnum.UNUSED.value)
            .where(CodeModel.code_id >= start_code_id)
            .where(CodeModel.code_id <= end_code_id)
            .where(CodeModel.prefix == prefix)
        )
        codes_count = await db.fetch_val(statement)
        print("codes_count", codes_count)
        print("count", count)
        if codes_count != count:
            raise AppException(HttpResp.SYSTEM_ERROR, msg="不可用")

        return ResultAPI(msg="可用", show=True)

    async def get_code_statistics_by_activity_id(self, activity_id: int):
        """
        根据activity_id获取codes信息（码号信息，统计数据）
        """
        codes = await self.code_orm.get_all_by_filters(activity_id=activity_id)
        return ResultAPI(
            data={
                "statistics": {
                    "total": len(codes),
                    "used": len(
                        [
                            code
                            for code in codes
                            if code.status == CodeStatusEnum.USED.value
                        ]
                    ),
                    "unused": len(
                        [
                            code
                            for code in codes
                            if code.status == CodeStatusEnum.UNUSED.value
                        ]
                    ),
                    "bound": len(
                        [
                            code
                            for code in codes
                            if code.status == CodeStatusEnum.BOUND.value
                        ]
                    ),
                },
                "codes": codes,
            }
        )

    async def rellot_prize(self, activity_id: int):
        """
        将活动的设置的码号和奖品进行关联
        logic
        1,查询数据：活动信息，奖品信息，码号信息
        2，汇总奖品和码号，活动奖品和码号批次都是关联存储。需要汇总出prize_lists 和 code_lists
        3，打乱奖品顺序，然后绑定到code上面

        """
        # 获取活动信息
        activity = await self.activity_orm.get_one_by_id(activity_id)
        if not activity:
            raise AppException(HttpResp.SYSTEM_ERROR, msg="活动不存在")
        activity_prize_list = await self.activity_prize_orm.get_all_by_filters(
            activity_id=activity_id
        )
        activity_code_no_list = await self.activity_code_no_orm.get_all_by_filters(
            activity_id=activity_id
        )
        # 汇总 prize_list code_list
        # 奖品
        prize_list: list[dict] = []
        for item in activity_prize_list:
            if item.num is None:
                continue
            prize_list.extend(
                [
                    {
                        "activity_id": item.activity_id,
                        "prize_id": item.prize_id,
                        "prize_level_id": item.prize_level_id,
                        "probability": item.probability,
                        "prize_index": item.prize_index,
                        "activity_prize_id": item.id,
                    }
                    for _ in range(item.num)
                ]
            )
        # 码号
        code_list: list[dict] = []
        for item in activity_code_no_list:
            prefix = item.prefix
            code_id_start = item.code_id_start
            code_id_end = item.code_id_end
            total_num = item.total_num
            where = and_(
                CodeModel.code_id >= code_id_start,
                CodeModel.code_id <= code_id_end,
                CodeModel.prefix == prefix,
                CodeModel.status == CodeStatusEnum.UNUSED.value,
                CodeModel.activity_id.is_(None),
            )
            temp_code_list = await self.code_orm.get_all_by_filters(where=where)
            code_list.extend([temp_code.model_dump() for temp_code in temp_code_list])

        # 打乱奖品顺序
        random.shuffle(prize_list)
        # 判断奖品数量和码号数量是否一致
        if len(prize_list) != len(code_list):
            raise AppException(HttpResp.SYSTEM_ERROR, msg="奖品数量和码号数量不一致")

        # 分配奖品
        for code, prize in zip(code_list, prize_list):
            await self.code_orm.update(
                id=code["id"],
                data={
                    "activity_id": activity.id,
                    "prize_id": prize["prize_id"],
                    "prize_index": prize["prize_index"],
                    "status": CodeStatusEnum.BOUND.value,
                },
            )
        return

    # async def rellot_prize_(
    #     self,
    #     prefix: str,
    #     start_code_id: int,
    #     end_code_id: int,
    #     activity_num: int,
    #     activity_prize_list: List[ActivityPrizeOutSchema],
    # ):
    #     """
    #     【弃用】
    #     分配奖品
    #     (关联码号和奖品)
    #     """

    #     where_clause = and_(
    #         CodeModel.activity_id.is_(None),
    #         CodeModel.status == CodeStatusEnum.UNUSED.value,
    #         CodeModel.prefix == prefix,
    #         CodeModel.code_id.between(start_code_id, end_code_id),
    #     )
    #     codes = await self.code_orm.get_all_by_filters(
    #         where=where_clause,
    #     )

    #     # 分配奖品
    #     prize_distribution = []
    #     for activity_prize in activity_prize_list:
    #         if activity_prize.num is None:
    #             continue

    #         prize_distribution.extend(
    #             [
    #                 {
    #                     "activity_id": activity_prize.activity_id,
    #                     "prize_id": activity_prize.prize_id,
    #                     "prize_level_id": activity_prize.prize_level_id,
    #                     "probability": activity_prize.probability,
    #                     "prize_index": activity_prize.prize_index,
    #                     "activity_prize_id": activity_prize.id,
    #                 }
    #                 for _ in range(activity_prize.num)
    #             ]
    #         )

    #     # 打乱奖品顺序
    #     random.shuffle(prize_distribution)

    #     # 绑定活动id，奖品到抽奖码
    #     for code, prize_info in zip(codes, prize_distribution):
    #         code = code.model_dump()
    #         code.update(prize_info)
    #         await self.code_orm.update(
    #             id=code["id"],
    #             data={
    #                 "activity_id": code.get("activity_id"),
    #                 "prize_id": code.get("prize_id"),
    #                 "prize_index": code.get("prize_index"),
    #                 "status": CodeStatusEnum.BOUND.value,
    #             },
    #         )

    async def comple_code_no(self, comple_code_no_in: CodeCompleCodeNoInSchema):
        """
        补全码编号，根据活动数量和起始id返回 列表【code_no_start,code_no_end,total_num】
        """
        prefix = comple_code_no_in.prefix
        code_no_start = comple_code_no_in.code_no_start
        num = comple_code_no_in.num
        remaining = num
        result_ranges = []
        current_start = code_no_start

        while remaining > 0:
            # 查询当前范围内第一个已被使用或绑定的码
            where = and_(
                CodeModel.prefix == prefix,
                CodeModel.code_id >= current_start,
                CodeModel.code_id <= current_start + remaining - 1,
                CodeModel.status.not_in([CodeStatusEnum.UNUSED.value]),
            )
            conflict_code = await self.code_orm.get_one_by_filters(where=where)

            # 修改后的代码片段
            if conflict_code is None:
                # 当前范围内没有冲突码，可以全部使用
                result_ranges.append(
                    {
                        "codeIdStart": current_start,
                        "codeIdEnd": current_start + remaining - 1,
                        "totalNum": remaining,
                        "prefix": prefix,
                        "text": f"{prefix}.{current_start} - {current_start + remaining - 1}   计数：{remaining}",
                    }
                )
                break
            else:
                conflict_id = conflict_code.code_id
                if conflict_id > current_start:
                    # 添加前面的可用区间
                    result_ranges.append(
                        {
                            "codeIdStart": current_start,
                            "codeIdEnd": conflict_id - 1,
                            "totalNum": conflict_id - current_start,
                            "prefix": prefix,
                            "text": f"{prefix}.{current_start} - {conflict_id - 1}  计数：{conflict_id - current_start}",
                        }
                    )
                    used_count = conflict_id - current_start
                    remaining -= used_count
                # 跳过冲突码后的下一个位置
                current_start = conflict_id + 1
        # 检测总数
        code_available_total = await self.code_orm.count_by_filters(
            where=and_(
                CodeModel.prefix == prefix,
                CodeModel.status == CodeStatusEnum.UNUSED.value,
                CodeModel.activity_id.is_(None),
            )
        )

        total_available = sum(item["totalNum"] for item in result_ranges)
        if code_available_total < total_available:
            raise AppException(exc=HttpResp.FAILED, msg="码号库存不足")
        if total_available != num:
            raise AppException(exc=HttpResp.FAILED, msg="总数与实际不符")
        return ResultAPI(data=result_ranges)
